package com.basker.pisces.domain.property.meta;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.basker.pisces.core.meta.IDataField;
import com.basker.pisces.core.meta.IDataObjectMeta;
import com.basker.pisces.core.meta.annotation.DataField;
import com.basker.pisces.domain.ISupportEntityBuild;
import com.basker.pisces.domain.constant.DomainConstants.ObjectErrorCode;
import com.basker.pisces.domain.entity.meta.Entity;
import com.basker.pisces.domain.property.InvalidPropertyValueException;
import com.basker.pisces.domain.value.StringValue;
import com.basker.pisces.resource.StringResources;
import com.basker.pisces.service.exception.IObjectErrorsWrapper.ObjectError;

/**
 * 文本属性
 *
 * @param <V>
 * @author hangwen
 */
public class TextProperty extends Property<StringValue> implements ISupportEntityBuild {

    @DataField
    private Integer maxLength = -1;

    @DataField
    private String regEx;

    private transient Pattern pattern;

    public TextProperty() {

    }

    @Override
    public void bindField(IDataObjectMeta objectMeta) {
        super.bindField(objectMeta);

        this.setMaxLengthWithDataFieldLength();
    }

    @Override
    public void build(Entity entity) {
        String regEx2Use = this.getRegEx();
        if (!StringUtils.isEmpty(regEx2Use)) {
            this.pattern = Pattern.compile(regEx2Use);
        }
    }

    public ObjectError createMaxLengthViolationError(Object dataObject, StringValue stringValueObject) {
        return new ObjectError(ObjectErrorCode.MAXLENGTH_VIOLATION, dataObject, null,
                this.getMaxLengthViolationMessgae(stringValueObject));
    }

    public ObjectError createRegexViolationError(Object dataObject, StringValue stringValueObject) {
        return new ObjectError(ObjectErrorCode.REGEX_VIOLATION, dataObject, null,
                this.getRegexViolationMessgae(stringValueObject));
    }

    @Override
    public StringValue createValue(Object rawValue) {
        if (rawValue == null || "".equals(rawValue)) {
            return StringValue.EMPTY;
        }

        return new StringValue(rawValue);
    }

    public Integer getMaxLength() {
        return maxLength;
    }

    public String getRegEx() {
        return regEx;
    }

    public void setMaxLength(Integer maxLength) {
        this.maxLength = maxLength;
    }

    public void setRegEx(String regEx) {
        this.regEx = regEx;
    }

    @Override
    public void setValue(Object dataObject, Object inputValue) {
        StringValue stringValueObject = this.createValue(inputValue);

        List<ObjectError> errors = this.validate(dataObject, stringValueObject);
        if (!CollectionUtils.isEmpty(errors)) {
            throw new InvalidPropertyValueException(errors);
        }

        this.setRawValue(dataObject, stringValueObject.getDataValue());
    }

    public boolean validateMaxLengh(StringValue propertyValue) {
        Assert.notNull(propertyValue, "parameter 'propertyValue' is required");

        if (propertyValue.isEmpty()) {
            return true;
        }

        Integer maxLength2Use = this.getMaxLength();
        if (maxLength2Use != null && maxLength2Use > 0) {
            Object value = propertyValue.getDataValue();
            return value == null || value.toString().length() <= maxLength2Use;
        }

        return true;
    }

    protected List<ObjectError> validate(Object dataObject, StringValue stringValueObject) {
        List<ObjectError> errors = null;

        if (!this.validateMaxLengh(stringValueObject)) {
            if (errors == null) {
                errors = new ArrayList<>();
            }

            errors.add(this.createMaxLengthViolationError(dataObject, stringValueObject));
        }

        if (!this.validateRegEx(stringValueObject)) {
            if (errors == null) {
                errors = new ArrayList<>();
            }

            errors.add(this.createRegexViolationError(dataObject, stringValueObject));
        }

        return errors;
    }

    private String getMaxLengthViolationMessgae(StringValue propertyValue) {
        return StringResources.get("pisces-domain.TextProperty.MaxLengthViolation", this.toString(),
                this.getMaxLength(), propertyValue);
    }

    private String getRegexViolationMessgae(StringValue propertyValue) {
        return StringResources.get("pisces-domain.TextProperty.RegExViolation", this.toString(), this.getRegEx(),
                propertyValue);
    }

    private void setMaxLengthWithDataFieldLength() {
        if (this.getMaxLength() > 0) {
            return;
        }

        IDataField dataField = this.tryGetDataField();
        if (dataField != null) {
            Integer length = dataField.getLength();
            if (length != null) {
                this.setMaxLength(length);
            }
        }
    }

    private boolean validateRegEx(StringValue propertyValue) {
        Assert.notNull(propertyValue, "parameter 'propertyValue' is required");

        if (this.pattern == null) {
            return true;
        }

        if (propertyValue.isEmpty()) {
            return true;
        }

        Object value = propertyValue.getDataValue();
        return value == null || this.pattern.matcher(value.toString()).matches();
    }

}
